package com.glsc.ngateway.platform.service.itflow;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.glsc.ngateway.common.api.common.enums.PlatformConstant;
import com.glsc.ngateway.common.api.common.utils.PageUtil;
import com.glsc.ngateway.common.api.flowable.dto.FlowDoTaskRespDto;
import com.glsc.ngateway.common.api.flowable.dto.param.ParamStopDto;
import com.glsc.ngateway.common.api.flowable.dto.task.FlowFormBaseEntityDto;
import com.glsc.ngateway.common.api.flowable.dto.task.config.ProcessEditConfigDto;
import com.glsc.ngateway.common.api.flowable.dto.task.doTaskParam.*;
import com.glsc.ngateway.common.api.flowable.dto.task.doTaskParam.system.SystemPassParamDto;
import com.glsc.ngateway.common.api.flowable.dto.task.doTaskParam.vendor.VendorRateFlowCreateParamDto;
import com.glsc.ngateway.common.api.flowable.dto.task.formData.AbstractTaskFormDataDto;
import com.glsc.ngateway.common.api.flowable.dto.task.formData.vendor.VendorRateTaskFormDataDto;
import com.glsc.ngateway.common.api.flowable.dto.task.formData.vendor.domaindto.VendorRateMainFormDto;
import com.glsc.ngateway.common.api.flowable.feign.IFeignFlowableService;
import com.glsc.ngateway.common.api.flowable.resp.FlowableResponse;
import com.glsc.ngateway.common.api.flowable.resp.ResponseMsgDto;
import com.glsc.ngateway.common.api.flowable.resp.ResponseSituationDto;
import com.glsc.ngateway.common.api.ldap.dto.LdapEhrDeptDto;
import com.glsc.ngateway.common.api.ldap.feign.IFeignLdapService;
import com.glsc.ngateway.common.api.ldap.resp.AppEhrResponse;
import com.glsc.ngateway.common.api.platform.dto.user.PlatformUserDto;
import com.glsc.ngateway.common.base.enums.itflow.ITFlowEnum;
import com.glsc.ngateway.common.base.enums.itflow.ITFlowTaskRoleEnum;
import com.glsc.ngateway.common.base.exception.PlatformException;
import com.glsc.ngateway.platform.service.FlowCommonService;
import com.glsc.ngateway.platform.service.UserService;
import com.glsc.ngateway.platform.service.itflow.aspect.ItFlowAuthentication;
import com.glsc.ngateway.platform.service.itflow.config.ProcessEditConfigService;
import com.glsc.ngateway.platform.service.itflow.flowstrategy.ItFlowStrategyProxyAdapter;
import com.glsc.ngateway.platform.utils.DateUtils;
import com.glsc.ngateway.platform.vo.FlowTaskParamPreVo;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;

import static com.glsc.ngateway.common.api.common.enums.DictConstant.ROLE_OPEARATOR;

/**
 * @author xc
 * @date 2023/01/11
 * 设备流程服务类
 */
@Service
public class ItFlowService {

    Logger logger = LoggerFactory.getLogger(ItFlowService.class);

    @Resource
    private IFeignFlowableService feignFlowableService;

    @Resource
    private FlowCommonService flowCommonService;

    @Resource
    private ProcessEditConfigService processEditConfigService;

    @Resource
    private UserService userService;

    @Resource
    private IFeignLdapService feignEhrService;

    @Resource
    private ItFlowStrategyProxyAdapter itFlowStrategyProxyAdapter;

    /**
     * 创建流程
     *
     * @param taskDto       流程创建发起对象
     * @param operatorStart 发起人
     * @return
     */
    public <D extends FlowFormBaseEntityDto, T extends AbstractTaskFormDataDto<D>> FlowDoTaskRespDto createFlow(@NotNull AbstractCreateParamDto<D, T> taskDto, @NotNull String operatorStart, @NotNull String flowKey) throws Exception {
        return itFlowStrategyProxyAdapter.createFlow(taskDto, operatorStart, flowKey);
    }

    /**
     * 保存节点数据，但是不提交任务，（不包括还未创建流程时，保存草稿的场景）
     */
    @ItFlowAuthentication
    public <D extends FlowFormBaseEntityDto, T extends AbstractTaskFormDataDto<D>> FlowDoTaskRespDto saveFormData(AbstractCreateParamDto<D, T> taskDto, String operator, @NotNull String flowKey) throws Exception {
        if (ObjectUtil.isNull(taskDto)
                || ObjectUtil.isNull(taskDto.getFlowableFormData())
                || StrUtil.isEmpty(operator)) {
            throw PlatformException.error("流程保存必填要素不能为空", false, false);
        }
        //TODO 检查其它必填要素
        FlowDoTaskRespDto respDto = StrUtil.isEmpty(taskDto.getTaskId()) ? new FlowDoTaskRespDto()
                : flowCommonService.validCheckTaskBeforeSaveFormOrAuditTask(taskDto.getTaskId());
        T flowableFormData = taskDto.getFlowableFormData();
        if (StrUtil.isNotEmpty(respDto.getProcessId())) {
            flowableFormData.setProcessId(respDto.getProcessId());
        }
        itFlowStrategyProxyAdapter.saveOrUpdateForm(taskDto, operator, flowKey);
        return respDto;
    }

    /**
     * 审核通过
     */
    @ItFlowAuthentication
    public <D extends FlowFormBaseEntityDto, T extends AbstractTaskFormDataDto<D>> FlowDoTaskRespDto taskAuditPass(AbstractPassParamDto<D, T> param, String operatorAccount, @NotNull String flowKey) throws Exception {
        return itFlowStrategyProxyAdapter.taskAuditPass(param, operatorAccount, flowKey);
    }


    /**
     * 审核驳回
     */
    @ItFlowAuthentication
    public FlowableResponse<ResponseMsgDto> taskAuditReject(AbstractRejectParamDto param, String operatorStart, @NotNull String flowKey) throws PlatformException {
        return itFlowStrategyProxyAdapter.reject(param, operatorStart, flowKey);
    }

    /**
     * 强制终止
     */
    @ItFlowAuthentication
    public void processStop(ParamStopDto paramStopDto, @NotBlank String flowKey, String operatorAccount) throws Exception {
        itFlowStrategyProxyAdapter.processStop(paramStopDto, flowKey, operatorAccount);
    }

    /**
     * 根据流程实例ID或者任务ID检索流程
     */
    @ItFlowAuthentication
    public <D extends FlowFormBaseEntityDto, T extends AbstractTaskFormDataDto<D>, R extends AbstractCreateParamDto<D, T>>
    R getWorkFlowFormByTaskId(@NotNull AbstractFindByIdParamDto param, @NotNull String flowKey, @NotNull R context) {
        String taskId = param.getTaskId(), processId = param.getProcessId();
        if (StrUtil.isEmpty(taskId) && StrUtil.isEmpty(processId)) {
            throw PlatformException.error("查询流程时，需要提供流程实例Id或者任务Id和流程节点标识", false, false);
        }
        ResponseSituationDto currentTaskInfo = null;
        //流程结束或者抄送查看流程时，仅根据processId检索要素信息
        if (StrUtil.isEmpty(taskId)) {
            context.setProcessId(processId);
            context.setHandleProcessMode(PlatformConstant.FLOWABLE_HANDLE_MODE_VIEW);
            //减轻前段判空处理
            context.setCurrentTaskInfo(new ResponseSituationDto());
        } else {
            taskId = StrUtil.trim(taskId);
            context.setHandleProcessMode(PlatformConstant.FLOWABLE_HANDLE_MODE_EDIT);
            FlowableResponse<ResponseSituationDto> flowableTaskInfoResponse = flowCommonService.getTaskInfo(taskId, PlatformConstant.SYS_ID_GATEWAY);
            if (flowableTaskInfoResponse.getStatus() != 0) {
                throw PlatformException.error(String.format("流程引擎未查询到任务信息: %s", taskId), true, false);
            } else {
                currentTaskInfo = flowableTaskInfoResponse.getData();
                //task 任务详情
                context.setCurrentTaskInfo(currentTaskInfo);
                context.setProcessId(currentTaskInfo.getProcessId());
                //具体任务方式查看，需要taskId/taskDefinitionKey
                context.setTaskId(currentTaskInfo.getCurrentTaskId());
                context.setTaskDefinitionKey(currentTaskInfo.getCurrentTaskDefinitionKey());
                context.setProcessDefineKey(currentTaskInfo.getProcessDefineKey());
                //加载任务节点信息
                ProcessEditConfigDto editConfigDto = processEditConfigService.getProcessEditConfigDto(context.getProcessDefineKey(), context.getTaskDefinitionKey());
                context.setProcessEditConfigDto(editConfigDto);
            }
        }
        //查询表单数据
        T flowFormEquipment = itFlowStrategyProxyAdapter.findFormByProcessId(context, flowKey);
        context.setFlowableFormData(flowFormEquipment);

        return context;
    }

    public Page<ResponseSituationDto> getProcessSituationPage(
            AbstractFlowSearchParamDto param, String account, @NotNull String flowKey) {
        LocalDateTime start = DateUtils.formatDateToLocalDateTime(DateUtils.formatDateString_(param.getBeginDate()));
        LocalDateTime end = StringUtils.isBlank(param.getEndDate()) ? null : DateUtils.formatDateToLocalDateTime(DateUtils.formatDateString_(param.getEndDate())).plusDays(1);
        param.setBeginDateLocalDate(start);
        param.setEndDateLocalDate(end);
        boolean isAdmin = false;
        //根据部门信息，找到所属部门下的所有人员
        if (param.getDeptId() != null && userService.userRoleContain(ITFlowTaskRoleEnum.EQUIPMENT_ADMIN.getRoleName(), account)) {
            //获取所有部门
            AppEhrResponse<List<LdapEhrDeptDto>> depts = feignEhrService.getAllDept();
            List<LdapEhrDeptDto> deptinfoEntityList = Objects.nonNull(depts) && CollectionUtils.isNotEmpty(depts.getData()) ? depts.getData() : Collections.EMPTY_LIST;
            Map<Integer, LdapEhrDeptDto> deptMap = deptinfoEntityList.stream().collect(Collectors.toMap(LdapEhrDeptDto::getDepid, Function.identity(), (K1, K2) -> K2));
            LdapEhrDeptDto dept = deptMap.get(param.getDeptId());
            if (dept != null) {
                String leader = dept.getLeader();
                List<String> accoutList = userService.findAllByLeaderAccount(leader);
                param.setAdminList(accoutList);
            }
        } else if (userService.userRoleContain(ITFlowTaskRoleEnum.EQUIPMENT_ADMIN.getRoleName(), account)
                || userService.userRoleContain("admin", account)
                || userService.userRoleContain(ROLE_OPEARATOR, account)) {
            //管理员能看所有
            param.setAdminList(Collections.emptyList());
            isAdmin = true;
        } else {
            //非管理员，看自己和下属
            List<String> accoutList = userService.findAllByLeaderAccount(account);
            param.setAdminList(accoutList);
        }
        //查询
        //分页参数初始化
        Pageable pageable = PageRequest.of(PageUtil.initPageNo(param.getPageNo()) - 1, PageUtil.initPageSize(param.getPageSize()));
        //查询人时，先检查是否有权限
        if (StringUtils.isNotBlank(param.getStartUser())) {
            //此人在权限列表中，直接查
            if (isAdmin || param.getAdminList().contains(param.getStartUser())) {
                param.setAdminList(java.util.Collections.singletonList(param.getStartUser()));
            } else {
                return new PageImpl<>(java.util.Collections.emptyList(), pageable, 0L);
            }
        }
        //获取ProcessIds
        List<String> processIds = itFlowStrategyProxyAdapter.pageProcessId(flowKey, param, pageable);
        //通过结果processId再查询flow信息
        List<ResponseSituationDto> process = processIds.stream()
                .map(r -> feignFlowableService.getProcessInfo(r).getData())
                .collect(Collectors.toList());
        return new PageImpl<>(process, pageable, processIds.size());
    }

    /**
     * 预览发起流程各种信息
     */
    public FlowTaskParamPreVo preViewCreateFlow(String flowKey, String creator) {
        PlatformUserDto user = userService.findByUsername(creator);
        return itFlowStrategyProxyAdapter.preViewCreateFlow(flowKey, user);
    }


    @Async
    public void batchAudit(List<String> taskIdlist,String operatorStart){
        for(String item : taskIdlist){
            try {
                AbstractFindByIdParamDto paramDto = new AbstractFindByIdParamDto();
                paramDto.setTaskId(item);
                VendorRateFlowCreateParamDto<VendorRateMainFormDto, VendorRateTaskFormDataDto> context = new VendorRateFlowCreateParamDto<>();
                this.getWorkFlowFormByTaskId(paramDto, ITFlowEnum.VENDOR_RATE.getKey(), context);
                SystemPassParamDto<VendorRateMainFormDto, VendorRateTaskFormDataDto> temp = new SystemPassParamDto<>();
                BeanUtils.copyProperties(context,temp);
                this.taskAuditPass(temp, operatorStart, ITFlowEnum.VENDOR_RATE.getKey());
            } catch (Exception e) {
                logger.info("批量审批内部异常，{}",item,e);
            }
        }
    }
}
